package com.mac.hosp_pre.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mac.hosp_pre.admin.dto.ScheduleOrderRespDto;
import com.mac.hosp_pre.admin.feign.HospitalFeignClient;
import com.mac.hosp_pre.except.ResultCodeEnum;
import com.mac.hosp_pre.except.ServiceException;
import com.mac.hosp_pre.mq.MqConst;
import com.mac.hosp_pre.mq.OrderMqDto;
import com.mac.hosp_pre.order.entity.OrderInfo;
import com.mac.hosp_pre.order.enums.OrderStatusEnum;
import com.mac.hosp_pre.order.mapper.OrderMapper;
import com.mac.hosp_pre.order.model.req.OrderCountQueryReqDto;
import com.mac.hosp_pre.order.model.req.OrderQueryReqDto;
import com.mac.hosp_pre.order.model.resp.OrderCountRespDto;
import com.mac.hosp_pre.order.model.resp.OrderInfoRespDto;
import com.mac.hosp_pre.order.service.OrderService;
import com.mac.hosp_pre.order.service.WeixinService;
import com.mac.hosp_pre.order.util.SMSUtils;
import com.mac.hosp_pre.reply.R;
import com.mac.hosp_pre.user.dto.PatientSimpRespDto;
import com.mac.hosp_pre.user.feign.PatientFeignClient;
import com.mac.hosp_pre.util.IdGenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    @Autowired
    private SMSUtils smsUtils;

    @Autowired
    private PatientFeignClient patientFeignClient;

    @Autowired
    private HospitalFeignClient hospitalFeignClient;

    @Resource
    private RocketMQTemplate rocketMQTemplate;



    @Transactional(rollbackFor = Exception.class)
    @Override
    public String saveOrder(String scheduleId, Long patientId) {
        //获取就诊人信息
        R<PatientSimpRespDto> patient = patientFeignClient.getPatient(patientId);
        if (!patient.ifSuccess() || patient.getData() == null){
            throw new ServiceException(ResultCodeEnum.NOT_EXISTS_ERROR.getCode(), patient.getMessage());
        }
        PatientSimpRespDto patientData = patient.getData();

        //获取排班相关信息
        R<ScheduleOrderRespDto> schedule = hospitalFeignClient.getScheduleOrder(scheduleId);
        if (!schedule.ifSuccess() || schedule.getData() == null){
            throw new ServiceException(ResultCodeEnum.NOT_EXISTS_ERROR.getCode(), schedule.getMessage());
        }
        ScheduleOrderRespDto scheduleData = schedule.getData();

        Date date = new Date();
        //判断当前时间是否还可以预约
        if(scheduleData.getStartTime().after(date)
                || scheduleData.getEndTime().before(date)) {
            throw new ServiceException(ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode(), "当前时间不能预约");
        }

        //添加到订单表
        OrderInfo orderInfo = new OrderInfo();
        //scheduleOrderVo 数据复制到 orderInfo
        BeanUtils.copyProperties(scheduleData,orderInfo);
        orderInfo.setId(null);
        //向orderInfo设置其他数据
        orderInfo.setOutTradeNo(IdGenUtils.snowflakeStrId(""));
        orderInfo.setScheduleId(scheduleId);
        orderInfo.setUserId(patientData.getUserId());
        orderInfo.setPatientId(patientId);
        orderInfo.setNumber(1);
        orderInfo.setPatientName(patientData.getName());
        orderInfo.setPatientPhone(patientData.getPhone());
        orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());
        baseMapper.insert(orderInfo);
        //发送消息
        OrderMqDto orderMq = new OrderMqDto();

        rocketMQTemplate.asyncSendOrderly(MqConst.ORDER_TOPIC+":"+MqConst.ORDER_TAG_MASTER,  MessageBuilder.withPayload(orderMq).build(), MqConst.ORDER_TOPIC, new SendCallback(){
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("订单消息-订单id:{},消息ID:{},发送成功", orderInfo.getId(),sendResult.getMsgId());
            }

            @Override
            public void onException(Throwable e) {
                log.error("订单消息-订单id:{},发送异常:{}", orderInfo.getId(),e.getMessage());
            }
        });
        //延迟订单消费，为了指定时间内没有支付，自动取消订单
        rocketMQTemplate.asyncSend(MqConst.ORDER_TOPIC+":"+MqConst.ORDER_TAG_DELAY,  MessageBuilder.withPayload(orderMq).build(), new SendCallback(){
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("订单延迟消息-订单id:{},消息ID:{},发送成功", orderInfo.getId(),sendResult.getMsgId());
            }

            @Override
            public void onException(Throwable e) {
                log.error("订单延迟消息-订单id:{},发送异常:{}", orderInfo.getId(),e.getMessage());
            }
        },rocketMQTemplate.getProducer().getSendMsgTimeout(), 14);//延迟10分钟
        return orderInfo.getOutTradeNo();
    }

    @Override
    public OrderInfoRespDto getOrder(String orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        if (orderInfo == null) {
            throw new ServiceException(ResultCodeEnum.NOT_EXISTS_ERROR);
        }
        OrderInfoRespDto respDto = new OrderInfoRespDto();
        BeanUtils.copyProperties(orderInfo, respDto);
        this.fillOrderInfo(respDto);
        return respDto;
    }

    @Override
    public IPage<OrderInfoRespDto> selectPage(OrderQueryReqDto orderQuery) {
        String name = orderQuery.getKeyword(); //医院名称
        Long patientId = orderQuery.getPatientId(); //就诊人名称
        String orderStatus = orderQuery.getOrderStatus(); //订单状态
        String reserveDate = orderQuery.getReserveDate();//安排时间
        String createTimeBegin = orderQuery.getCreateTimeBegin();
        String createTimeEnd = orderQuery.getCreateTimeEnd();
        Page<OrderInfo> page = Page.of(orderQuery.getPageNo(), orderQuery.getPageSize());

        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(name), OrderInfo::getHospName, name)
                .eq(null != patientId, OrderInfo::getPatientId, patientId)
                .eq(StringUtils.isNotBlank(orderStatus), OrderInfo::getOrderStatus, orderStatus)
                .eq(StringUtils.isNotBlank(reserveDate), OrderInfo::getReserveDate, reserveDate)
                .ge(StringUtils.isNotBlank(createTimeBegin), OrderInfo::getCreateTime, createTimeBegin)
                .le(StringUtils.isNotBlank(createTimeEnd), OrderInfo::getCreateTime, createTimeEnd)
                .orderByDesc(OrderInfo::getId);
        IPage<OrderInfo> selectPage = baseMapper.selectPage(page, wrapper);
        if (CollectionUtil.isNotEmpty(selectPage.getRecords())){
            return selectPage.convert(item -> {
                OrderInfoRespDto orderInfoRespDto = new OrderInfoRespDto();
                BeanUtils.copyProperties(item, orderInfoRespDto);
                this.fillOrderInfo(orderInfoRespDto);
                return orderInfoRespDto;
            });
        }
        return Page.of(orderQuery.getPageNo(), orderQuery.getPageSize());
    }

    @Override
    public Boolean cancelOrder(Long orderId) {
        //获取订单信息
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        if (orderInfo == null){
            throw new ServiceException(ResultCodeEnum.NOT_EXISTS_ERROR);
        }
        //判断是否取消
        if(orderInfo.getQuitTime().before(new Date())) {
            throw new ServiceException(ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode(),"当前时间不能退款");
        }
        //判断当前订单是否可以取消
        if(orderInfo.getOrderStatus() == OrderStatusEnum.PAID.getStatus().intValue()) {
            //微信退款
            Boolean isRefund = SpringUtil.getBean(WeixinService.class).refund(orderId);
            if(!isRefund) {
                throw new ServiceException(ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode(),"退款失败");
            }
            //更新订单状态
            OrderInfo oInfo = new OrderInfo();
            oInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
            oInfo.setId(orderId);
            baseMapper.updateById(oInfo);

            //TODO: 发送mq
        }else {
            throw new ServiceException(ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode(),"当前状态不能取消");
        }

        return true;
    }

    @Override
    public void patientTips() {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getReserveDate,new DateTime().toString("yyyy-MM-dd"));
        wrapper.ne(OrderInfo::getOrderStatus,OrderStatusEnum.CANCLE.getStatus());
        List<OrderInfo> orderInfoList = baseMapper.selectList(wrapper);
        for(OrderInfo orderInfo : orderInfoList) {
            //短信提示
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") +"/"+ (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHospName()+"|"+orderInfo.getDeptName()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            //发送短信
            smsUtils.sendMessage(orderInfo.getPatientPhone(),param);
        }
    }

    @Override
    public Map<String, Object> getCountMap(OrderCountQueryReqDto orderCountQuery) {
        List<OrderCountRespDto> orderCountVoList = baseMapper.selectOrderCount(orderCountQuery);

        //获取x需要数据 ，日期数据  list集合
        List<String> dateList = orderCountVoList.stream().map(OrderCountRespDto::getReserveDate).collect(Collectors.toList());

        //获取y需要数据，具体数量  list集合
        List<Integer> countList =orderCountVoList.stream().map(OrderCountRespDto::getCount).collect(Collectors.toList());

        Map<String,Object> map = new HashMap<>();
        map.put("dateList",dateList);
        map.put("countList",countList);
        return map;
    }

    private void fillOrderInfo(OrderInfoRespDto orderInfo) {
        orderInfo.setOrderStatusString(OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus()));
    }
}


